using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using Needle.Utils;

namespace Needle.Core.Injection
{

    /// <summary>
    /// Describes a dependency injection point.
    /// </summary>
    public interface IInjectionSite : IComparable<IInjectionSite>
    {
        /// <summary>
        /// the ordinal for injection
        /// </summary>
        int Order { get; }
        /// <summary>
        /// the method to use for injection
        /// </summary>
        MethodBase Method { get; }
        /// <summary>
        /// the parameters to use for injection.
        /// </summary>
        IList<IParameter> Parameters { get; }
        /// <summary>
        /// The dependencies for this injection site.
        /// </summary>
        IEnumerable<InstanceId> Dependencies { get; }
    }
    /// <summary>
    /// Default implementation of IInjectionSite.
    /// </summary>
    [DebuggerDisplay("InjectionSite {ToString()}")]
    public class InjectionSite : IInjectionSite
    {

        /// <summary>
        /// inintializes a new InjectionSite instance 
        /// </summary>
        public InjectionSite(MethodBase member, int order)
            : this(member, order, GetParameters(member))
        {
        }

        /// <summary>
        /// inintializes a new InjectionSite instance 
        /// </summary>
        /// <param name="member"></param>
        /// <param name="order">The order of injection, or -1 for a constructor.</param>
        /// <param name="parameters"></param>
        public InjectionSite(MethodBase member, int order, IList<IParameter> parameters)
        {
            Ensure.NotNull(member, "member");
            Ensure.GreaterThan(-2, order, "order");
            Ensure.NotNull(parameters, "parameters");

            m_Method = member;
            if (!parameters.IsReadOnly)
            {
                IParameter[] arr = new IParameter[parameters.Count];
                parameters.CopyTo(arr, 0);
                m_Parameters = new ReadOnlyCollection<IParameter>(arr);
            }
            else
            {
                m_Parameters = parameters;
            }
            m_Order = order;
        }

        #region IInjectionSite Members

        private MethodBase m_Method;
        /// <summary>
        /// the method to use for injection.
        /// </summary>
        public MethodBase Method
        {
            get { return m_Method; }
        }

        private IList<IParameter> m_Parameters;
        /// <summary>
        /// The parameters. to use for injection.
        /// </summary>
        public IList<IParameter> Parameters
        {
            get { return m_Parameters; }
        }

        private int m_Order;
        /// <summary>
        /// The ordinal of this site in the injection sequence.
        /// </summary>
        public int Order
        {
            get { return m_Order; }
        }

        /// <summary>
        /// The dependencies for this injection site.
        /// </summary>
        public IEnumerable<InstanceId> Dependencies
        {
            get
            {
                foreach (IParameter p in m_Parameters)
                    yield return p.Dependency;
            }
        }

        #endregion
        /// <summary>
        /// return a string representation
        /// </summary>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(Method.DeclaringType.FullName);
            sb.Append("::");
            sb.Append(Method.Name);
            sb.Append("(");
            foreach (IParameter p in Parameters)
            {
                sb.Append(p.Info.ParameterType.Name);
                sb.Append(" ");
                sb.Append(p.Info.Name);
                sb.Append(", ");
            }
            if (Parameters.Count > 0)
            {
                sb.Length -= 2;
            }
            sb.Append(")");
            sb.Append(" Order = ");
            sb.Append(Order);
            return sb.ToString();
        }
        /// <summary>
        /// Returns a list of iparameters for every argument of the method.
        /// </summary>
        public static IList<IParameter> GetParameters(MethodBase method)
        {
            InjectAttribute inject = InjectAttribute.GetFrom(method);
            string depName = null;
            if (inject != null)
            {
                depName = inject.Name;
            }

            ParameterInfo[] infos = method.GetParameters();
            if (infos.Length > 0)
            {
                IParameter[] parameters = new IParameter[infos.Length];
                for (int i = 0; i < infos.Length; i++)
                {
                    parameters[i] = new Parameter(infos[i], depName);
                }
                return new ReadOnlyCollection<IParameter>(parameters);
            }

            return Parameter.EmptyParameters;
        }

        #region IComparable<IInjectionSite> Members

        /// <summary></summary>
        public int CompareTo(IInjectionSite other)
        {
            int result = this.Order - other.Order;
            if (result == 0)
                result = String.CompareOrdinal(this.Method.Name, other.Method.Name);
            return result;
        }

        #endregion
    }

}
